/*
  Ejemplo de programa servidor. Atiende peticiones aritmeticas, el protocolo de transporte sera HTTP
  ver http::Transport y el contenido del mensaje sera el resutaldo de un comm::Codec con los
  (x, y, op) -> El resultado sera estos tres componente mas result.

  Para poder probar el sistema de congestion se puede indicar un numero de milisegundos de
  retardo aplicados a cada contestacion.

  Los clientes pueden ser: http_kclient.p
*/
#include <iostream>

#include <nemesis.h>
#include <nemesis.comm.h>

#include <nemesis.http.Request.h>
#include <nemesis.http.Response.h>
#include <nemesis.http.Handler.h>
#include <nemesis.http.Transport.h>
#include <nemesis.http.functions.h>
#include <nemesis.http.sccs.h>
#include <nemesis.sccs.h>

nemesis_import_sccs_tag (http);

using namespace std;

class MyHandler : public http::Handler {
public:
   MyHandler () : http::Handler ("http_echo::MyHandler") { 
      a_httpResponse.createHeader (http::Header::Type::Date); 
      a_httpResponse.createHeader (http::Header::Type::Server)->setValue (nemesis_use_sccs_tag (http));
   }

   void setNotSend (const bool notSend) throw () { a_notSend = notSend; }

private:
   http::Response a_httpResponse;
   bool a_notSend;

   void evRequest (ClientSocket&, const http::Request& request) throw (RuntimeException);
   void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException) {;}
};

class MyCommunicator : public comm::Communicator {
public:
   MyCommunicator () {;}

   void setNotSend (const bool notSend) throw () { a_httpHandler.setNotSend (notSend); }

private:
   MyHandler a_httpHandler;

   void eventReceiveMessage (comm::ClientSocket &, const Message& message)
      throw (RuntimeException);
};

class HTTPArithmeticServer : public comm::Application {
public:
   HTTPArithmeticServer ();
      
private:
   MyCommunicator a_communicator;
   comm::ServerSocket* a_serverSocket;

   void initialize () throw (RuntimeException);
   void run () throw (RuntimeException);
};

using namespace std;
using namespace nemesis::comm;

int main (int argc, const char** argv)
{
   CommandLine& commandLine (CommandLine::instanciate ());
   HTTPArithmeticServer app;

   http::functions::initialize ();

   srand (time (NULL));

   try {
      commandLine.initialize (argv, argc);
      commandLine.verify ();

      Logger::setLevel (Logger::Debug); 
      Logger::initialize ("http_echo", new nemesis::TraceWriter ("file.trace", 4048000));
 
      app.start ();
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }
   
   return 0;
}

HTTPArithmeticServer::HTTPArithmeticServer () : 
   Application ("http_echo", "Servidor de echo", "1.0") 
{
   CommandLine& commandLine (CommandLine::instanciate ());
      
   commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que atender peticiones");
   commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP en la que atender");
   commandLine.add ("r", CommandLine::Argument::Optional, "Indicador de reuso de direccin", false);
   commandLine.add ("notsend", CommandLine::Argument::Optional, "Indicador de no responder al mensaje", false);
}

void HTTPArithmeticServer::initialize () 
   throw (RuntimeException)
{
   CommandLine& cl (CommandLine::instanciate ());

   int port = cl.getIntegerValue ("p");
   const comm::Device* device = Network::instanciate ().find (Device::asAddress (cl.getValue ("a")));

   a_serverSocket = new ServerSocket (INetAddress (device, port), cl.exists ("r"), &http::Transport::getFactory ());

   a_communicator.setNotSend (cl.exists ("notsend"));
}

void HTTPArithmeticServer::run ()
   throw (RuntimeException)
{
   a_communicator.attach (a_serverSocket);
   a_communicator.accept ();
}

void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", FILE_LOCATION));

   if (clientSocket.support (http::Transport::className ()) == false)
      return;

   static int messageCounter = 0;
   static int successCounter = 0;

   int value;

   CongestionController& congestionController = CongestionController::instanciate ();

   messageCounter ++;

   if (congestionController.getAdvice (clientSocket) == CongestionController::Advice::Discard)
      return;

   successCounter ++;

   a_httpHandler.apply (clientSocket, message);
}

void MyHandler::evRequest (ClientSocket& clientSocket, const http::Request& request)
   throw (RuntimeException)
{
   const DataBlock& body = request.getBody ();

   LOGINFORMATION (
      string msg ("Body recibido: ");
      msg += nemesis::functions::asString (body);
      Logger::information (msg, FILE_LOCATION);
   );

   LOGINFORMATION (
      const http::Header* header;
      string msg;

      for (http::Request::const_header_iterator ii = request.header_begin (), maxii = request.header_end (); ii != maxii; ii ++) {
         header = http::Request::header (ii);
         Logger::information (header->asString (), FILE_LOCATION);
      }

      if ((header = request.find (http::Header::Type::Connection)) != NULL) {
         if (header->match ("keep-alive", http::Header::Compare::FullMode))
            Logger::information ("Keep Alive activado en la conexion", FILE_LOCATION);
         else
            Logger::information ("Keep Alive NO activado en la conexion", FILE_LOCATION);
      }
   );

   if (a_notSend == true)
      return;

   a_httpResponse.clearBody ();
   a_httpResponse.find (http::Header::Type::Date)->setValue ("Mon, 30 Jan 2006 14:36:18 GMT");

   http::Header* userData = a_httpResponse.find ("UserData");
 
   if (userData == NULL)
      userData = a_httpResponse.createHeader ("UserData");

   userData->setValue ("Verificacio del cambio 1.0.7");
   a_httpResponse.setBody (body);

   try {
      clientSocket.send (a_httpResponse);
   }
   catch (Exception& ex) {
      ex.trace ();
   }
}

